package fr.kptn.musixmatchwrapper.http;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;

import com.google.gson.Gson;
import com.google.gson.JsonParseException;

import fr.kptn.musixmatchwrapper.dto.IResponse;
import fr.kptn.musixmatchwrapper.dto.error.ErrorResponse;
import fr.kptn.musixmatchwrapper.exception.MusixmatchWrapperException;
import fr.kptn.musixmatchwrapper.util.Constants;
import fr.kptn.musixmatchwrapper.util.StatusCode;

/**
 * Utility class to send requests to the musiXmatch API and to decode responses.
 * 
 * @author Sylvain
 * 
 */
public class MusixmatchRequest {

	/**
	 * Send a request to the musixMatch API
	 * 
	 * @param pRequestString
	 *            the string representing the request to be send. Only the part
	 *            about the method is stored in this parameter. The base URL and
	 *            the API version are concatenated (at the beginning) to this
	 *            parameter to obtain the full request path
	 * @param pClass
	 *            the class of the response
	 * @return the full response decoded
	 * @throws MusixmatchWrapperException
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Object sendRequest(String pRequestString, Class pClass) throws MusixmatchWrapperException {
		StringBuffer lResponseSB = new StringBuffer();
		try {
			String lCompleteRequestString = Constants.API_URL + Constants.URL_DELIMITER + Constants.API_VERSION + Constants.URL_DELIMITER + pRequestString;

			URL lRequestedURL = new URL(lCompleteRequestString);

			BufferedReader lReader = new BufferedReader(new InputStreamReader(lRequestedURL.openStream(), Constants.API_ENCODING));
			String lCurrentLine;

			lCurrentLine = lReader.readLine();
			while (lCurrentLine != null) {
				lResponseSB.append(lCurrentLine);

				lCurrentLine = lReader.readLine();
			}
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		Gson lConverter = new Gson();
		IResponse lResponse = null;

		try {
			handleErrorResponse(lResponseSB.toString(), null);
			lResponse = (IResponse) lConverter.fromJson(lResponseSB.toString(), pClass);
		} catch (JsonParseException lJpe) {
			handleErrorResponse(lResponseSB.toString(), lJpe);
		}

		return lResponse;
	}

	/**
	 * This method is used to handle the response of a request to the musiXmatch
	 * API.<br/><br/>
	 * 
	 * If the status code of the response is different from 200 (success code) a
	 * <code>MusixmatchWrapperException</code> exception is thrown with the
	 * corresponding message.<br/><br/>
	 * 
	 * If the status code of the response is 200 (success code) but there has
	 * been an error in decoding the JSON response, another
	 * <code>MusixmatchWrapperException</code> is thrown. The message of this
	 * exception contains the information about an error during the JSON
	 * decoding operation.<br/><br/>
	 * 
	 * If the response cannot be decoded at all the same exception as the
	 * preceding is thrown.<br/><br/>
	 * 
	 * @param pResponse
	 *            the full response as a string
	 * @param pJsonParseException
	 *            the potentiel JSON parsing exception thrown when decoding the
	 *            response
	 * @throws MusixmatchWrapperException
	 */
	private static void handleErrorResponse(String pResponse, JsonParseException pJsonParseException) throws MusixmatchWrapperException {
		Gson lConverter = new Gson();
		ErrorResponse lErrorResponse = null;

		try {
			lErrorResponse = lConverter.fromJson(pResponse, ErrorResponse.class);

			if (lErrorResponse != null && lErrorResponse.getHeader() != null) {
				int lResponseCode = lErrorResponse.getHeader().getStatusCode();
				String lResponseMessage = null;

				switch (lResponseCode) {
				case StatusCode.SUCCESS_STATUS_CODE:
					if (pJsonParseException != null) {
						throw new MusixmatchWrapperException("An error occured during the JSON parsing operation", pJsonParseException);
					}
					break;
				case StatusCode.BAD_SYNTAX_STATUS_CODE:
					lResponseMessage = StatusCode.BAD_SYNTAX_MESSAGE;
					break;
				case StatusCode.AUTHENTICATION_FAILED_STATUS_CODE:
					lResponseMessage = StatusCode.AUTHENTICATION_FAILED_MESSAGE;
					break;
				case StatusCode.LIMIT_REACHED_STATUS_CODE:
					lResponseMessage = StatusCode.LIMIT_REACHED_MESSAGE;
					break;
				case StatusCode.NOT_AUTHORIZED_STATUS_CODE:
					lResponseMessage = StatusCode.NOT_AUTHORIZED_MESSAGE;
					break;
				case StatusCode.RESOURCE_NOT_FOUND_STATUS_CODE:
					lResponseMessage = StatusCode.RESOURCE_NOT_FOUND_MESSAGE;
					break;
				case StatusCode.METHOD_NOT_FOUND_STATUS_CODE:
					lResponseMessage = StatusCode.METHOD_NOT_FOUND_MESSAGE;
					break;
				default:
					lResponseMessage = StatusCode.UNKNOWN_ERROR_MESSAGE;
					break;
				}

				if (lResponseMessage != null) {
					throw new MusixmatchWrapperException(lResponseMessage);
				}
			}
		} catch (JsonParseException lJpe) {
			throw new MusixmatchWrapperException("An error occured during the JSON parsing operation", lJpe);
		}
	}
}
